package com.dankegongyu.lib.http.request;

import android.text.TextUtils;

import com.dankegongyu.lib.http.DKHttp;
import com.dankegongyu.lib.http.api.DKHttpApiService;
import com.dankegongyu.lib.http.func.ApiErrorFunction;
import com.dankegongyu.lib.http.func.ApiFunction;
import com.dankegongyu.lib.http.interceptor.DKHeaderInterceptor;
import com.dankegongyu.lib.http.model.DKHttpHeaders;
import com.google.gson.reflect.TypeToken;

import java.net.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;

/**
 * @author wupuquan
 * @version 1.0
 * @since 2018/6/13 14:21
 */
@SuppressWarnings({"unchecked", "WeakerAccess"})
public abstract class BaseRequest<T extends BaseRequest> {

    String url; // 完整url
    private OkHttpClient okHttpClient;
    private long connectTimeout;
    private long readTimeout;
    private long writeTimeout;
    private final List<Interceptor> interceptors = new ArrayList<>();
    private final List<Interceptor> networkInterceptors = new ArrayList<>();
    Map<String, Object> params = new HashMap<>(); // map不能为null，否则请求发不出去
    private DKHttpHeaders mHeaders;
    DKHttpApiService apiService;

    public BaseRequest(String url, OkHttpClient okHttpClient) {
        this.url = url;
        this.okHttpClient = okHttpClient;
    }

    public T setTimeout(long timeout) {
        this.connectTimeout = timeout;
        this.readTimeout = timeout;
        this.writeTimeout = timeout;
        return (T) this;
    }

    public T setConnectTimeout(long connectTimeout) {
        this.connectTimeout = connectTimeout;
        return (T) this;
    }

    public T setReadTimeout(long readTimeout) {
        this.readTimeout = readTimeout;
        return (T) this;
    }

    public T setWriteTimeout(long writeTimeout) {
        this.writeTimeout = writeTimeout;
        return (T) this;
    }

    public T addInterceptor(Interceptor interceptor) {
        if (interceptor != null) {
            this.interceptors.add(interceptor);
        }
        return (T) this;
    }

    public T addNetworkInterceptor(Interceptor networkInterceptor) {
        if (networkInterceptor != null) {
            this.networkInterceptors.add(networkInterceptor);
        }
        return (T) this;
    }

    public T param(String key, Object value) {
        // retrofit要求key和value都不能为空，
        // https://github.com/square/retrofit/commit/10ff3d97faadcd6f716447d3376f9f51992e44dd
        if (!TextUtils.isEmpty(key) && value != null) {
            if (value instanceof String) {
                if (!TextUtils.isEmpty((String) value)) {
                    this.params.put(key, value);
                }
            } else {
                this.params.put(key, value);
            }
        }
        return (T) this;
    }

    public T params(Map<String, Object> params) {
        if (params != null) {
            for (Map.Entry<String, Object> item : params.entrySet()) {
                String key = item.getKey();
                Object value = item.getValue();
                if (!TextUtils.isEmpty(key) && value != null) {
                    if (value instanceof String) {
                        if (!TextUtils.isEmpty((String) value)) {
                            this.params.put(key, value);
                        }
                    } else {
                        this.params.put(key, value);
                    }
                }
            }
        }
        return (T) this;
    }

    public T addHeader(String key, String value) {
        checkHeaders();
        mHeaders.addHeader(key, value);
        return (T) this;
    }

    public T addHeaders(Map<String, String> map) {
        checkHeaders();
        mHeaders.addHeaders(map);
        return (T) this;
    }

    public T removeHeader(String key) {
        checkHeaders();
        mHeaders.removeHeader(key);
        return (T) this;
    }

    public T removeAllHeaders() {
        checkHeaders();
        mHeaders.removeAllHeaders();
        return (T) this;
    }

    private void checkHeaders() {
        if (mHeaders == null) {
            mHeaders = new DKHttpHeaders();
        }
    }

    private OkHttpClient.Builder createOkHttpClientBuilder() {
        final OkHttpClient.Builder builder;
        if (okHttpClient != null) {
            builder = okHttpClient.newBuilder();
        } else {
            builder = new OkHttpClient.Builder();
        }
        if (!DKHttp.isDebugMode()) {
            // 防代理抓包
            builder.proxy(Proxy.NO_PROXY);
        }
        builder.retryOnConnectionFailure(true); // 错误重连
        if (this.connectTimeout > 0) {
            builder.connectTimeout(this.connectTimeout, TimeUnit.MILLISECONDS);
        }
        if (this.readTimeout > 0) {
            builder.readTimeout(this.readTimeout, TimeUnit.MILLISECONDS);
        }
        if (this.writeTimeout > 0) {
            builder.writeTimeout(this.writeTimeout, TimeUnit.MILLISECONDS);
        }
        // DKHttpHeaders加在最前面
        if (mHeaders != null) {
            builder.addInterceptor(new DKHeaderInterceptor(mHeaders));
        }
        for (Interceptor interceptor : this.interceptors) {
            builder.addInterceptor(interceptor);
        }
        for (Interceptor interceptor : this.networkInterceptors) {
            // 一般用不到，与addInterceptor区别：https://blog.csdn.net/lxk_1993/article/details/101291763
            builder.addNetworkInterceptor(interceptor);
        }
        return builder;
    }

    private Retrofit.Builder createRetrofit() {
        Retrofit.Builder builder = new Retrofit.Builder();
        // ConverterFactory
        // ScalarsConverterFactory返回原始数据，必须在GsonConverterFactory之前添加
        builder.addConverterFactory(ScalarsConverterFactory.create());
        // 默认添加GsonConverter
        builder.addConverterFactory(GsonConverterFactory.create());
        // CallAdapterFactory
        // 默认添加RxJava2CallAdapterFactory
        builder.addCallAdapterFactory(RxJava2CallAdapterFactory.create());
        return builder;
    }

    T build() {
        Retrofit.Builder retrofitBuilder = createRetrofit();
        // Retrofit必须设置baseUrl(must end in /)，现在用完整url，所以随便设置一个就行
        retrofitBuilder.baseUrl("https://www.baidu.com/");
        retrofitBuilder.client(createOkHttpClientBuilder().build());
        this.apiService = retrofitBuilder.build().create(DKHttpApiService.class);
        return (T) this;
    }

    /**
     * 返回未解析的原始字符串数据
     */
    public Observable<String> execute() {
        return build().newRequest()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .onErrorResumeNext(new ApiErrorFunction<String>());
    }

    public <R> Observable<R> execute(Class<R> clazz) {
        return build().newRequest()
                .map(new ApiFunction<>(clazz))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .onErrorResumeNext(new ApiErrorFunction<R>());
    }

    public <R> Observable<R> execute(TypeToken<R> typeToken) {
        return build().newRequest()
                .map(new ApiFunction<>(typeToken))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .onErrorResumeNext(new ApiErrorFunction<R>());
    }

    protected abstract Observable<String> newRequest();
}
